# coding = utf-8
import logging

from io import BytesIO
from django.db import transaction
from django.db.models import F, Q, Sum
from django.forms import model_to_dict
from django.http import HttpResponse
from django.utils.encoding import escape_uri_path
from rest_framework import status
from rest_framework.decorators import action
from rest_framework.generics import ListAPIView
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.viewsets import ModelViewSet
import pandas as pd
from openpyxl.utils import get_column_letter
from openpyxl.styles import Alignment, Font
from openpyxl.styles import Border, Side

from base.models import Company, CustomSupplier
from finance.api.utils import update_account
from purchase.api.utils import decrease_entries, increase_entries_stats
from purchase.models import WareHouseEntry, PurchasePayOrder, WareHouseEntryDetail, PurchasePayOrderDetail
from sale.models import SaleOrderDetail
from store.api.utils import decrease_goods_stocks

from .serializers import WareHouseEntrySerializer, PurchasePayOrderSerializer, WareHouseEntryDetailSerializer, \
    PurchasePayOrderDetailSerializer

logger = logging.getLogger("django")


class WareHouseEntryListAPIView(ListAPIView):
    """入库单列表"""
    permission_classes = ()
    queryset = WareHouseEntry.objects.filter(Q(flag=0) | Q(flag=1)).exclude(status=1)
    serializer_class = WareHouseEntrySerializer
    pagination_class = None

    def get_queryset(self):
        queryset = super().get_queryset()

        name = self.request.query_params.get("name", None)
        supplier_id = self.request.query_params.get("supplier", None)

        if name:
            queryset = queryset.filter(code__contains=name)

        if supplier_id:
            queryset = queryset.filter(supplier_id=supplier_id)

        return queryset


class WareHouseEntryDetailViewSet(ModelViewSet):
    """入库明细"""
    permission_classes = ()
    queryset = WareHouseEntryDetail.objects.all()
    serializer_class = WareHouseEntryDetailSerializer
    pagination_class = None

    def get_queryset(self):
        queryset = super().get_queryset()
        return queryset

    @action(methods=['delete'], detail=False)
    def batch_delete(self, request, *args, **kwargs):
        select_ids = request.data.get("select_ids", None)
        if not select_ids:
            msg = {"status_code": status.HTTP_404_NOT_FOUND, "message": "删除失败!"}
            return Response(data=msg, status=status.HTTP_404_NOT_FOUND)
        with transaction.atomic():
            save_id = transaction.savepoint()
            try:
                self.update_goods_stock(select_ids)
                self.serializer_class.Meta.model.objects.filter(id__in=select_ids).delete()
            except Exception as e:
                print(e)
                transaction.savepoint_rollback(save_id)

            transaction.savepoint_commit(save_id)

        msg = {"status_code": status.HTTP_204_NO_CONTENT, "message": "删除成功!"}
        return Response(data=msg, status=status.HTTP_204_NO_CONTENT)

    def update_goods_stock(self, ids):
        objs = self.serializer_class.Meta.model.objects.filter(id__in=ids)
        increase_entries_stats(objs, -1)
        # 减库存
        decrease_goods_stocks(objs)
        self.update_return_nums(objs)

    def update_return_nums(self, objs):
        for obj in objs:
            if obj.sale_detail_id:
                SaleOrderDetail.objects.filter(id=obj.sale_detail_id).update(return_nums=F('return_nums') - obj.nums)


class WareHouseEntryViewSet(ModelViewSet):
    """入库单"""
    permission_classes = ()
    queryset = WareHouseEntry.objects.all()
    serializer_class = WareHouseEntrySerializer
    pagination_class = None

    def get_queryset(self):
        queryset = super().get_queryset()

        flag = self.request.query_params.get("flag", None)
        code = self.request.query_params.get("code", None)
        search_date = self.request.query_params.getlist("search_date[]", None)
        supplier_id = self.request.query_params.get("supplier", None)
        buyer_id = self.request.query_params.get("buyer", None)

        if flag:
            queryset = queryset.filter(flag=int(flag))

        if code:
            queryset = queryset.filter(code__contains=code)

        if search_date and len(search_date) == 2:
            queryset = queryset.filter(warehouse_time__range=(search_date[0], search_date[1]))

        if supplier_id:
            queryset = queryset.filter(supplier_id=int(supplier_id))

        if buyer_id:
            queryset = queryset.filter(buyer_id=int(buyer_id))

        return queryset

    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()
        with transaction.atomic():
            save_id = transaction.savepoint()
            try:
                increase_entries_stats(instance.lists.all(), -1)
                decrease_goods_stocks(instance.lists.all())
                if instance.flag == 3:  # 还回登记
                    self.update_return_nums(instance.lists.all())

                if instance.imprest:
                    update_account(instance.account.id, (-1) * instance.imp_amount)

                self.perform_destroy(instance)

            except Exception as e:
                print(e)
                transaction.savepoint_rollback(save_id)

            transaction.savepoint_commit(save_id)

        return Response(status=status.HTTP_204_NO_CONTENT)

    @action(methods=['delete'], detail=False)
    def batch_delete(self, request, *args, **kwargs):
        select_ids = request.data.get("select_ids", None)
        if not select_ids:
            msg = {"status_code": status.HTTP_404_NOT_FOUND, "message": "删除失败!"}
            return Response(data=msg, status=status.HTTP_404_NOT_FOUND)

        with transaction.atomic():
            save_id = transaction.savepoint()
            try:
                self.update_goods_stock(select_ids)
                self.serializer_class.Meta.model.objects.filter(id__in=select_ids).delete()
            except Exception as e:
                print(e)
                transaction.savepoint_rollback(save_id)

            transaction.savepoint_commit(save_id)

        msg = {"status_code": status.HTTP_204_NO_CONTENT, "message": "删除成功!"}
        return Response(data=msg, status=status.HTTP_204_NO_CONTENT)

    def update_goods_stock(self, ids):
        entries = self.serializer_class.Meta.model.objects.filter(id__in=ids)
        for entry in entries:
            if entry.imprest:
                update_account(entry.account.id, (-1) * entry.imp_amount)

            increase_entries_stats(entry.lists.all(), -1)
            decrease_goods_stocks(entry.lists.all())
            if entry.flag == 3:
                self.update_return_nums(entry.lists.all())

    def update_return_nums(self, objs):
        for obj in objs:
            if obj.sale_detail_id:
                SaleOrderDetail.objects.filter(id=obj.sale_detail_id).update(return_nums=F('return_nums') - obj.nums)


class PurchasePayOrderViewSet(ModelViewSet):
    """采购付款单"""
    permission_classes = ()
    queryset = PurchasePayOrder.objects.all()
    serializer_class = PurchasePayOrderSerializer
    pagination_class = None

    def get_queryset(self):
        queryset = super().get_queryset()

        code = self.request.query_params.get("name", None)
        search_date = self.request.query_params.getlist("search_date[]", None)
        supplier_id = self.request.query_params.get("supplier", None)
        user_id = self.request.query_params.get("user", None)
        receivable = self.request.query_params.get("receivable", None)  # 应付款金额

        if code:
            queryset = queryset.filter(code__contains=code)

        if search_date and len(search_date) == 2:
            queryset = queryset.filter(pay_time__range=(search_date[0], search_date[1]))

        if supplier_id:
            queryset = queryset.filter(supplier_id=int(supplier_id))

        if user_id:
            queryset = queryset.filter(user_id=int(user_id))

        if receivable:
            queryset = queryset.filter(receivable__gte=receivable)

        return queryset

    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()
        with transaction.atomic():
            save_id = transaction.savepoint()
            try:
                if not instance.entry_code:
                    update_account(instance.account_id, -instance.actual_amount)
                    decrease_entries(instance.details.all())
                    self.perform_destroy(instance)
            except Exception as e:
                print(e)
                transaction.savepoint_rollback(save_id)

            transaction.savepoint_commit(save_id)

        return Response(status=status.HTTP_204_NO_CONTENT)

    @action(methods=['delete'], detail=False)
    def batch_delete(self, request, *args, **kwargs):
        select_ids = request.data.get("select_ids", None)
        if not select_ids:
            msg = {"status_code": status.HTTP_404_NOT_FOUND, "message": "删除失败!"}
            return Response(data=msg, status=status.HTTP_404_NOT_FOUND)

        with transaction.atomic():
            save_id = transaction.savepoint()
            try:
                self.update_entries(select_ids)
                self.serializer_class.Meta.model.objects.filter(id__in=select_ids).delete()
            except Exception as e:
                print(e)
                transaction.savepoint_rollback(save_id)

            transaction.savepoint_commit(save_id)

        msg = {"status_code": status.HTTP_204_NO_CONTENT, "message": "删除成功!"}
        return Response(data=msg, status=status.HTTP_204_NO_CONTENT)

    def update_entries(self, ids):
        pay_orders = self.serializer_class.Meta.model.objects.filter(id__in=ids)
        for order in pay_orders:
            if not order.entry_code:
                update_account(order.account_id, -order.actual_amount)
                decrease_entries(order.details.all())


class PurchasePayOrderDetailViewSet(ModelViewSet):
    """付款单明细"""
    permission_classes = ()
    queryset = PurchasePayOrderDetail.objects.all()
    serializer_class = PurchasePayOrderDetailSerializer
    pagination_class = None

    def get_queryset(self):
        queryset = super().get_queryset()
        return queryset

    @action(methods=['delete'], detail=False)
    def batch_delete(self, request, *args, **kwargs):
        select_ids = request.data.get("select_ids", None)
        if not select_ids:
            msg = {"status_code": status.HTTP_404_NOT_FOUND, "message": "删除失败!"}
            return Response(data=msg, status=status.HTTP_404_NOT_FOUND)
        with transaction.atomic():
            save_id = transaction.savepoint()
            try:
                self.update_entries(select_ids)
                self.serializer_class.Meta.model.objects.filter(id__in=select_ids).delete()
            except Exception as e:
                print(e)
                transaction.savepoint_rollback(save_id)

            transaction.savepoint_commit(save_id)

        msg = {"status_code": status.HTTP_204_NO_CONTENT, "message": "删除成功!"}
        return Response(data=msg, status=status.HTTP_204_NO_CONTENT)

    def update_entries(self, ids):
        objs = self.serializer_class.Meta.model.objects.filter(id__in=ids)
        # 减库存
        decrease_entries(objs)


class PurchaseBillAPIView(APIView):
    """采购入库单对账表"""
    permission_classes = ()

    def get(self, request, *args, **kwargs):
        supplier_id = self.request.query_params.get("supplier")
        search_date = self.request.query_params.getlist('search_date[]')
        export = request.query_params.get("export")
        print(export)
        supplier = None
        entries = WareHouseEntry.objects.filter(Q(flag=0) | Q(flag=1))
        queryset = WareHouseEntryDetail.objects.filter(Q(entry__flag=0) | Q(entry__flag=1))

        if supplier_id:
            queryset = queryset.filter(entry__supplier_id=int(supplier_id))
            entries = entries.filter(supplier_id=int(supplier_id))
            # 供应商
            supplier = CustomSupplier.objects.filter(id=int(supplier_id)).first()

        if search_date and len(search_date) == 2:
            queryset = queryset.filter(entry__warehouse_time__range=(search_date[0], search_date[1]))
            entries = entries.filter(warehouse_time__range=(search_date[0], search_date[1]))

        # 本公司信息
        company = Company.objects.first()

        totals_result = entries.aggregate(total_amount=Sum('total_amount'), amount=Sum('amount'), nopay_amount=Sum(F('total_amount') - F('amount')))
        totals_nums = queryset.aggregate(total_nums=Sum('nums'))
        totals_result["total_nums"] = totals_nums.get('total_nums') or 0

        serializer = WareHouseEntryDetailSerializer(queryset, many=True)

        if export == "1":
            bio = BytesIO()
            writer = pd.ExcelWriter(bio, engine='openpyxl')
            df = pd.DataFrame(serializer.data, columns=['entry_code', 'goods_code', 'goods_name', 'goods_specs', 'entry_time', 'nums', 'price', 'total_amount'])

            df.rename(columns={'entry_code': '入库单编号', 'goods_code': '商品编号', 'goods_specs': '规格',
                               'goods_name': '商品名称', 'entry_time': '入库时间',
                               'nums': '入库数量', 'price': '单价', 'total_amount': '金额'}, inplace=True)
            series = pd.Series({'入库单编号': '合计', '入库数量': totals_result["total_nums"], '金额': totals_result["total_amount"], '已付金额': totals_result["amount"], '未付金额': totals_result["nopay_amount"]})
            series = series.to_frame()
            s2 = pd.DataFrame(series.values.T, columns=series.index)

            df = pd.concat([df, s2], axis=0, ignore_index=True)

            df.to_excel(writer, sheet_name='入库对账单', index=False, float_format='%.2f')

            ws = writer.book.active     # excel sheet

            align = Alignment(horizontal='center', vertical='center', wrap_text=True)
            align_text = Alignment(vertical='center', wrap_text=True)
            # 插入行
            ws.insert_rows(1, 5)
            for i in range(1, 6):
                # 合并
                ws.merge_cells(start_row=i, end_row=i, start_column=1, end_column=ws.max_column)
                # 对齐方式
                ws['A' + str(i)].alignment = align
            # 设置行高
            for row in range(1, ws.max_row + 1):
                ws.row_dimensions[row].height = 25
            # 设置列宽
            for col in range(1, ws.max_column + 1):
                ws.column_dimensions[get_column_letter(col)].width = 18 if col == 1 or col == 3 else 12
            # 设置边框线
            border = Border(left=Side(border_style='thin', color='000000'),
                            right=Side(border_style='thin', color='000000'),
                            top=Side(border_style='thin', color='000000'),
                            bottom=Side(border_style='thin', color='000000'))

            for row in ws.iter_rows(min_row=ws.min_row + 5, max_row=ws.max_row, max_col=ws.max_column):
                for cell in row:
                    cell.border = border
                    cell.alignment = align_text
                    cell.number_format = '0.00'
            # 设置字体
            font = Font(size=16, bold=True)
            font_small = Font(size=9)
            ws['A1'].font = font
            ws['A1'].value = company.name
            ws['A2'].font = font_small
            ws['A2'].value = '公司地址:{} TEL:{} FAX: {}'.format(company.address, company.telephone, company.fax)

            ws['A3'].value = '对账单'
            ws['A3'].font = font

            ws['A4'].font = font_small
            ws['A4'].value = '对账日期:{}至{}'.format(search_date[0], search_date[1])
            if supplier:
                ws['A5'].font = font_small
                ws['A5'].value = '供应商:{}           联系人:{}           传真:{}'.format(supplier.name, supplier.linkman, supplier.fax)
            writer.save()

            response = HttpResponse(bio.getvalue(), content_type="application/octet-stream")
            supplier_name = supplier.name if supplier else ""
            file_name = '{}至{}{}对账单.xlsx'.format(search_date[0], search_date[1], supplier_name)
            response['Content-Disposition'] = "attachment;filename={}".format(escape_uri_path(file_name.encode('utf-8')))
            return response
        results = {
            'company': model_to_dict(company) if company else None,
            'supplier': model_to_dict(supplier) if supplier else None,
            'data': serializer.data,
            'sums': totals_result
        }

        return Response(results)

